package com.lmabbe.common.datasources.plugin;

import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.lmabbe.common.global.context.LmabbeSessionUser;
import com.lmabbe.common.global.context.session.LoginUser;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.mapping.StatementType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.context.ApplicationContext;

import java.sql.Connection;
import java.util.stream.Collectors;

/**
 * @author lmabbe
 */
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
@Slf4j
@RequiredArgsConstructor
public class DataScopePlugin implements Interceptor {

    private final ApplicationContext applicationContext;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        log.debug("===> data-scope-plugin ===>");
        LmabbeSessionUser sessionUser = applicationContext.getBean(LmabbeSessionUser.class);
        if (sessionUser == null || sessionUser.loginUser() == null) {
            log.debug("用户尚未登录，在执行系统sql");
            return invocation.proceed();
        }
        LoginUser loginUser = sessionUser.loginUser();


        StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget());
        MetaObject metaObject = SystemMetaObject.forObject(statementHandler);

        // 先判断是不是SELECT操作, 跳过存储过程
        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
        if (SqlCommandType.SELECT != mappedStatement.getSqlCommandType()
                || StatementType.CALLABLE == mappedStatement.getStatementType()) {
            return invocation.proceed();
        }

        // 针对定义了rowBounds，做为mapper接口方法的参数
        BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");
        String sql = null;
        switch (loginUser.getDataScope()) {
            case SELF:
                sql = "select * from (" + boundSql.getSql() + ") as data_scope_temp where data_scope_temp.create_by = " + loginUser.getUserId();
                break;
            case CUSTOMIZE:
                String collect = loginUser.getCustomDataScope()
                        .parallelStream()
                        .map(Object::toString)
                        .collect(Collectors.joining(","));
                sql = "select * from (" + boundSql.getSql() + ") as data_scope_temp where data_scope_temp.org_id in (" + collect + ")";
                break;
            case THIS_LEVEL:
                sql = "select * from (" + boundSql.getSql() + ") as data_scope_temp where data_scope_temp.org_id = " + loginUser.getCustomDataScope().get(0);
                break;
            case THIS_LEVEL_CHILDREN:
                sql = "select * from (" + boundSql.getSql() + ") as data_scope_temp inner join sys_org so on so.org_id = data_scope_temp.org_id" +
                        " where so.valid = 1 and JSON_CONTAINS(so.tree_path,\"" + loginUser.getCustomDataScope().get(0) + "\")";
                break;
            case UNKNOWN:
                throw new RuntimeException("用户权限异常，请赋予用户权限");
        }
        if (sql != null) {
            metaObject.setValue("delegate.boundSql.sql", sql);
        }
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        if (target instanceof StatementHandler) {
            return Plugin.wrap(target, this);
        }
        return target;
    }
}
